package org.gdstash.db;

import org.gdstash.file.ARZRecord;
import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.sql.*;
import java.util.LinkedList;
import java.util.List;

public class DBEngineLevel {
    private static final String TABLE_NAME = "GDC_LEVEL";
    private static final int ROW_LEVEL = 1;
    private static final int ROW_STAT_POINTS = 2;
    private static final int ROW_SKILL_POINTS = 3;
    private static DBEngineLevel singleton;
    List<LevelStats> levels;

    public DBEngineLevel() {
        this.levels = new LinkedList<LevelStats>();
    }

    private DBEngineLevel(final ARZRecord record) {
        this.levels = record.getLevelStatList();
    }

    public int getStatPointsForRange(final int start, final int end) {
        int min = start;
        int max = end;
        if (min > max) {
            final int temp = min;
            min = max;
            max = temp;
        }
        --max;
        --min;
        if (max > this.levels.size()) {
            max = this.levels.size();
        }
        if (min < 0) {
            min = 0;
        }
        int sum = 0;
        for (final LevelStats stats : this.levels) {
            if (stats.level >= max) {
                break;
            }
            if (stats.level < min) {
                continue;
            }
            sum += stats.statPoints;
        }
        return sum;
    }

    public int getSkillPointsForRange(final int start, final int end) {
        int min = start;
        int max = end;
        if (min > max) {
            final int temp = min;
            min = max;
            max = temp;
        }
        --max;
        --min;
        if (max > this.levels.size()) {
            max = this.levels.size();
        }
        if (min < 0) {
            min = 0;
        }
        int sum = 0;
        for (final LevelStats stats : this.levels) {
            if (stats.level >= max) {
                break;
            }
            if (stats.level < min) {
                continue;
            }
            sum += stats.skillPoints;
        }
        return sum;
    }

    public static void createTable() throws SQLException {
        final String dropTable = "DROP TABLE GDC_LEVEL";
        final String createTable = "CREATE TABLE GDC_LEVEL (LEVEL        INTEGER NOT NULL, STAT_POINTS  INTEGER, SKILL_POINTS INTEGER, PRIMARY KEY (LEVEL))";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final Statement st = conn.createStatement()) {
                if (GDDBUtil.tableExists(conn, "GDC_LEVEL")) {
                    st.execute(dropTable);
                }
                st.execute(createTable);
                st.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {"GDC_LEVEL"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_CREATE_TABLE", args);
                GDMsgLogger.addError(msg);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    public static void delete() throws SQLException {
        final String deleteEntry = "DELETE FROM GDC_LEVEL";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(deleteEntry)) {
                ps.executeUpdate();
                ps.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
        }
    }

    public static void insert(final ARZRecord record) throws SQLException {
        if (!record.getFileName().equals("records/creatures/pc/playerlevels.dbr")) {
            return;
        }
        DBEngineLevel.singleton = null;
        final DBEngineLevel entry = get();
        if (entry != null && entry.levels != null && !entry.levels.isEmpty()) {
            return;
        }
        final DBEngineLevel player = new DBEngineLevel(record);
        if (player.levels == null) {
            return;
        }
        if (player.levels.isEmpty()) {
            return;
        }
        final String insert = "INSERT INTO GDC_LEVEL VALUES (?,?,?)";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(insert)) {
                for (final LevelStats stats : player.levels) {
                    ps.setInt(1, stats.level);
                    ps.setInt(2, stats.statPoints);
                    ps.setInt(3, stats.skillPoints);
                    ps.executeUpdate();
                }
                ps.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_IN_PLAYER_CONFIG"));
                GDMsgLogger.addError(ex);
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    public static DBEngineLevel get() {
        if (DBEngineLevel.singleton != null) {
            return DBEngineLevel.singleton;
        }
        DBEngineLevel.singleton = new DBEngineLevel();
        final String command = "SELECT * FROM GDC_LEVEL ORDER BY LEVEL";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                DBEngineLevel.singleton.levels = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_READ_CONFIG_PLAYER"));
            GDMsgLogger.addError(ex2);
            DBEngineLevel.singleton = null;
        }
        return DBEngineLevel.singleton;
    }

    private static List<LevelStats> wrap(final ResultSet rs) throws SQLException {
        final List<LevelStats> list = new LinkedList<LevelStats>();
        while (rs.next()) {
            final LevelStats stats = new LevelStats();
            stats.level = rs.getInt(1);
            stats.statPoints = rs.getInt(2);
            stats.skillPoints = rs.getInt(3);
            list.add(stats);
        }
        return list;
    }

    public static void reset() {
        DBEngineLevel.singleton = null;
    }

    public static class LevelStats {
        private int level;
        private int statPoints;
        private int skillPoints;

        public int getLevel() {
            return this.level;
        }

        public int getStatPoints() {
            return this.statPoints;
        }

        public int getSkillPoints() {
            return this.skillPoints;
        }

        public void setLevel(final int level) {
            this.level = level;
        }

        public void setStatPoints(final int statPoints) {
            this.statPoints = statPoints;
        }

        public void setSkillPoints(final int skillPoints) {
            this.skillPoints = skillPoints;
        }
    }
}
